﻿//////////////////////////////////////////////////////////////////////////////
//
//  Copyright © 1998-2024 Glodon Company Limited.  All rights reserved.
//
//  Use of this software is subject to the terms of the Glodon license
//  agreement provided at the time of installation or download, or which
//  otherwise accompanies this software in either electronic or hard copy form.  
//
//////////////////////////////////////////////////////////////////////////////

// Reviewed

#pragma once

#include "GcmpModelInterface.h"
#include "INoElementIdDbObject.h"
#include "NdbObjectSchematic.h"
#include "DbDataSchema.h"
#include "GraphicsNodeId.h"
#include "GraphicsNodeVisibility.h"
#include <map>

namespace gcmp {

    class IDocument;
    class IElement;
    class IModelView;
    class IGraphicsElementShape; 
    class IGraphicsNodeReference;
    class IModelViewPreChangeData;
    class IPlanRegion;
    
    /// \brief 渲染优先级偏移的回调接口，用于在视图中重载 element 的渲染优先级偏移
    class GCMP_MODEL_INTERFACE_EXPORT IRenderPriorityOffsetProvider : public INoElementIdDbObject
    {
        DEFINE_CAST_DERIVED(gcmp::IRenderPriorityOffsetProvider, gcmp::INoElementIdDbObject)
    public:
        /// \brief 平台会通过这个接口获取重载的渲染优先级偏移
        /// \param pModelView 目标视图
        /// \param element 目标构件
        /// \param isOffsetValid 说明是否返回了有效的渲染优先级偏移值，也就是 offset 是否有效
        /// \param offset 构件在视图中重载的渲染优先级偏移
        virtual void GetRenderPriorityOffset(const IModelView* pModelView, const IElement& element, bool& isOffsetValid, Byte& offset) const = 0;
    };

    /// \brief 图形节点可见性重载回调接口，用于在视图中重载 element 节点可见性重载设置
    class GCMP_MODEL_INTERFACE_EXPORT IGraphicsNodeVisibilityCustomizer : public INoElementIdDbObject
    {
        DEFINE_CAST_DERIVED(gcmp::IGraphicsNodeVisibilityCustomizer, gcmp::INoElementIdDbObject)
    public:
        typedef std::vector<GraphicsNodeId> GraphicsNodeIdArray;
        class GCMP_MODEL_INTERFACE_EXPORT CustomizationInfo
        {
        public:
            /// \brief 节点路径定制可见性
            /// \param nodeRef 节点引用数据，需要保证有NodeIdPath
            /// \param visibility 可见性类型
            void CustomizeElementVisibilityByGraphicsNodeReference(const IGraphicsNodeReference& nodeRef, const GraphicsNodeVisibility& visibility);

            /// \brief根据节点路径获取定制可见性
            /// \return 名称定制的可见性
            const std::map<GraphicsNodeIdArray, GraphicsNodeVisibility>& GetCustomizedElementVisibilitiesByGraphicsNodeIdPaths() const;
        private:
            class GraphicsOverrideVisiblityCustomization
            {
            public:
                GraphicsOverrideVisiblityCustomization();
            public:
                void SetNodeIdPathToVisibility(const IGraphicsNodeReference& nodeRef, const GraphicsNodeVisibility& visibility);
                const std::map<GraphicsNodeIdArray, GraphicsNodeVisibility>& GetNodeIdPathToVisibility() const;
            private:
                std::map<GraphicsNodeIdArray, GraphicsNodeVisibility> m_nodeIdPathToVisiblityMap;
            };
        private:
            GraphicsOverrideVisiblityCustomization m_graphicsOverrideVisibilityCustomization;
        };

    public:
        virtual CustomizationInfo GetCustomizationInfo(const IModelView* pModelView) const = 0;
    };

    /// \brief Element视图相关的行为
    class GCMP_MODEL_INTERFACE_EXPORT IElementViewSpecificShapeComponent : public INoElementIdDbObject
    {
        DEFINE_CAST_DERIVED(gcmp::IElementViewSpecificShapeComponent, gcmp::INoElementIdDbObject);

    public:
        /// \brief 触发重新生成视图GRep的数据结构
        ///
        struct RecreateShapeInfo
        {
            /// \brief 模型视图发生改变的事件类型
            /// 
            enum
            {
                ModelViewScaleChanged = 0,       ///< 视图比例发生变化
                ModelViewDetailLevelChanged = 1  ///< 视图详细程度发生变化
            }Type;

            OwnerPtr<IModelViewPreChangeData> ModelViewPreChangeData; ///< 模型视图相关数据发生改变前的数据指针

            std::wstring ChangeTypeToWString() const
            {
                std::wstring changeType = L"";
                switch (Type)
                {
                case gcmp::IElementViewSpecificShapeComponent::RecreateShapeInfo::ModelViewScaleChanged:
                    changeType = L"ModelViewScaleChange";
                    break;
                case gcmp::IElementViewSpecificShapeComponent::RecreateShapeInfo::ModelViewDetailLevelChanged:
                    changeType = L"ModelViewDetailLevelChanged";
                    break;
                default:
                    DBG_WARN(L"不支持的类型", L"GMEP", L"2022-08-26");
                    break;
                }

                return changeType;
            }
        };

        enum DisplayTypeWhenAcrossViewRegion : int8_t
        {
            CutByAll = 0,                  ///< 按各自所在范围显示（正常切割）
            CutByModelViewRange = 1,       ///< 按主视图显示 （不受平面区域影响）
            CutByOnePlanRegion = 2   ///< 按所选平面区域的视图范围去显示  
        };
        enum ViewSpecificShapeStatus : int8_t
        {
            NotSureWhetherHasViewShape = 0,  ///< 不确定是否有视图Grep
            HasViewShape = 1,              ///< 有视图Grep
            HasNoViewShape = 2          ///< 没有视图Grep
        };

    public:
        /// \brief 设置该行为所属Element
        /// \param pOwnerElement 所属Element
        /// \return bool
        virtual bool SetOwnerElement(IElement* pOwnerElement) override = 0;

        /// \brief 获得该行为所属的Element
        /// \return 所属的Element
        virtual IElement* GetOwnerElement() override = 0;

        /// \brief 获得该行为所属的Element
        /// \return 所属的Element
        virtual const IElement* GetOwnerElement() const override = 0;

        /// \brief 创建Element视图相关行为
        /// 
        /// 创建得到的视图相关行为接口的行为如下：
        /// 1、IsVisibleInModelView：返回所属Element的可见性，即 GetOwnerElement()->IsVisible()
        /// 2、CreateViewSpecificElementShape：返回nullptr，框架随即会采用 ModelShape
        /// 3、IsCuttableByModelViewRange：返回true
        /// \return 创建的视图相关行为
        static OwnerPtr<IElementViewSpecificShapeComponent>
            CreateDefaultViewSpecificShapeComponent();

        /// \brief 创建所属Element为空的视图相关行为
        /// 
        /// 创建得到的视图相关行为接口的行为如下：
        /// 1、IsVisibleInModelView：返回所属Element的可见性，即 GetOwnerElement()->IsVisible()
        /// 2、CreateViewSpecificElementShape：返回nullptr，框架随即会采用 ModelShape
        /// 3、IsCuttableByModelViewRange：返回true
        /// \return 创建的视图相关行为
        static OwnerPtr<IElementViewSpecificShapeComponent> CreateNullViewSpecificShapeComponent();

    public:
        /// \brief Element是否在视图中可见
        ///    注意：注释类对象在视图内默认可见，在注释裁剪范围内根据注释显示规则可见。
        /// \param pModelView 目标视图
        /// \return true-可见，false-不可见
        virtual bool IsVisibleInModelView(const IModelView* pModelView) const = 0;
        
        /// \brief Element创建视图相关的图形表达
        /// \param pModelView 目标视图
        /// \return 视图相关的图形表达
        virtual OwnerPtr<IGraphicsElementShape> CreateViewSpecificElementShape(const IModelView* pModelView) const = 0;

        /// \brief Element是否支持视图范围切割
        /// \param pModelView 目标视图
        /// \return true-支持，false-不支持
        virtual bool IsCuttableByModelViewRange(const IModelView* pModelView) const = 0;

        /// \brief 汇报依赖的视图GReps所属的ElementIds
        ///    注意：注释类对象在视图内默认可见，在注释裁剪范围内，根据注释规则显示，对象可以汇报依赖的host依赖为自己，或者其他对象
        ///              视图更新显示节点，以及注释的显示规则将依赖dependentElementIds对象的显示。
        /// \param dependentElementIds 依赖的视图GReps所属的ElementIds
        /// \return void
        virtual void ReportDependentElements(std::vector<ElementId>& dependentElementIds) const = 0;

        /// \brief 是否需要重新生成视图GRep
        ///   注意：目前只有以下事件类型会触发此回调：ScaleChanged视图比例发生变化 DetailLevelChanged视图详细程度发生变化
        /// \param pModelView 目标视图
        /// \param shapeInfo 触发重新生成视图GRep的数据
        virtual bool NeedsRecreateViewSpecificElementShape(const IModelView* pModelView, const RecreateShapeInfo& shapeInfo) const;

        /// \brief 设置Element与平面区域相交时的显示设置, 默认返回CutByAll
        ///  注：index只在返回值为CutByCustomizePlanRegion 时有效
        /// \param pModelView 目标视图
        /// \param pGraphicsElementShape 要显示的Grep
        /// \param pAcrossPlanRegions 所有与grep相交的平面区域
        /// \param index返回值为 CutByCostomizePlaneViewRegion 时，选择一个平面区域的视图范围去显示
        /// \return Element与平面区域相交时的显示设置, 默认返回CutByAll
        virtual DisplayTypeWhenAcrossViewRegion GetTypeWhenAcrossViewRegion(const IModelView* pModelView, const IGraphicsElementShape* pGraphicsElementShape,
            const std::vector<const IPlanRegion*>& pAcrossPlanRegions, int& index) const;

        /// \brief 是否有视图Grep
        /// \param modelView 目标视图
        /// \param pViewGrepApproximateBox 视图Grep的粗包围盒的指针。当返回结果为HasViewShape且pViewGrepApproximateBox不为空时，需要返回视图Grep的粗包围盒，该包围盒等于或大于视图Grep的真实包围盒
        /// \return ViewSpecificShapeStatus，接口默认值为NotSureWhetherHasViewShape，该值仅限于平台使用，不推荐API用户使用
        /// \note 如果无法快速获取视图Grep的粗包围盒，可返回一个空的包围盒（Box3d::MakeEmpty())
        virtual ViewSpecificShapeStatus HasViewSpecificShape(const IModelView& modelView, Box3d* pViewGrepApproximateBox) const;
    
        /// \brief 设置渲染优先级偏移回调
        ///
        /// 默认实现为空，会报 DBG_WARN，需要派生类同时实现以下三个函数：Set|GetRenderPriorityOffsetProvider
        /// \param opProvider 回调对象
        virtual void SetRenderPriorityOffsetProvider(OwnerPtr<IRenderPriorityOffsetProvider> opProvider);

        /// \brief 获取渲染优先级偏移回调
        ///
        /// 默认实现为返回 nullptr，需要派生类同时实现以下三个函数：Set|GetRenderPriorityOffsetProvider
        /// \return 回调对象
        virtual const IRenderPriorityOffsetProvider* GetRenderPriorityOffsetProvider() const;

        /// \brief 获取渲染优先级偏移回调
        ///
        /// 默认实现为返回 nullptr，需要派生类同时实现以下三个函数：Set|GetRenderPriorityOffsetProvider
        /// \return 回调对象
        virtual IRenderPriorityOffsetProvider* GetRenderPriorityOffsetProvider();

        /// \brief 获取是否是多段注释对象
        /// \return 返回true，不是多段注释对象；false，普通注释对象。默认返回false
        /// \note 多段注释对象指注释对象的节点依赖的主体对象的节点不完全相同，如连续标注
        virtual bool IsMultiAnnotation() const;

        /// \brief 设置节点可见性重载回调
        ///
        /// 默认实现为空，会报 DBG_WARN，如果需要，派生类可以同时实现以下三个函数：Set|GetGraphicsNodeVisibilityCustomizer
        /// \param opCustomizer 重载回调对象
        virtual void SetGraphicsNodeVisibilityCustomizer(OwnerPtr<IGraphicsNodeVisibilityCustomizer> opCustomizer);

        /// \brief 设置节点可见性重载回调
        ///
        /// 默认实现为返回 nullptr，如果需要，派生类同时实现以下三个函数：Set|GetGraphicsNodeVisibilityCustomizer
        /// \return 重载回调对象
        virtual const IGraphicsNodeVisibilityCustomizer* GetGraphicsNodeVisibilityCustomizer() const;

        /// \brief 设置节点可见性重载回调
        ///
        /// 默认实现为返回 nullptr，如果需要，派生类同时实现以下三个函数：Set|GetGraphicsNodeVisibilityCustomizer
        /// \return 重载回调对象
        virtual IGraphicsNodeVisibilityCustomizer* GetGraphicsNodeVisibilityCustomizer();
    };

}// namespace gcmp


